﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="StyleCopOptions.cs" company="StyleCop for ReSharper Development Team">
//   Copyright (c) StyleCop for ReSharper Development Team. All rights reserved.
// </copyright>
// <summary>
//   Defines the StyleCopOptions type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

extern alias JB;

namespace StyleCop.For.ReSharper.Options
{
    #region Using Directives

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Windows.Forms;
    using System.Xml;

    using JetBrains.Application;
    using JetBrains.ComponentModel;
    using JetBrains.Util;

    using StyleCop.For.ReSharper.Core;
    using StyleCop.For.ReSharper.Properties;

    using MessageBox = System.Windows.Forms.MessageBox;

    #endregion

    /// <summary>
    /// Class to hold all of the Configurable options for this addin.
    /// </summary>
    [ShellComponentInterface(ProgramConfigurations.VS_ADDIN)]
    [ShellComponentImplementation]
    public class StyleCopOptions : IXmlExternalizableShellComponent
    {

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="StyleCopOptions"/> class. 
        /// Set to max performance by default.
        /// </summary>
        public StyleCopOptions()
        {
            this.ParsingPerformance = 9;
            this.SpecifiedAssemblyPath = String.Empty;
            this.InsertTextIntoDocumentation = true;
            this.AutomaticallyCheckForUpdates = true;
            this.AlwaysCheckForUpdatesWhenVisualStudioStarts = true;
            this.DaysBetweenUpdateChecks = 2;
            this.LastUpdateCheckDate = "1900-01-01";
            this.DashesCountInFileHeader = 116;
            this.UseExcludeFromStyleCopSetting = true;
            this.SuppressStyleCopAttributeJustificationText = "Reviewed. Suppression is OK here.";
            this.UseSingleLineDeclarationComments = false;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <value>
        /// The instance.
        /// </value>
        public static StyleCopOptions Instance
        {
            get
            {
                return Shell.Instance.GetComponent<StyleCopOptions>();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether AlwaysCheckForUpdatesWhenVisualStudioStarts.
        /// </summary>
        [JB::JetBrains.Util.XmlExternalizableAttribute(true)]
        public bool AlwaysCheckForUpdatesWhenVisualStudioStarts { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether we check for updates when plugin starts.
        /// </summary>
        [JB::JetBrains.Util.XmlExternalizableAttribute(true)]
        public bool AutomaticallyCheckForUpdates { get; set; }

        /// <summary>
        /// Gets or sets DashesCountInFileHeader.
        /// </summary>
        [JB::JetBrains.Util.XmlExternalizableAttribute(116)]
        public int DashesCountInFileHeader { get; set; }

        /// <summary>
        /// Gets or sets DaysBetweenUpdateChecks.
        /// </summary>
        [JB::JetBrains.Util.XmlExternalizableAttribute(2)]
        public int DaysBetweenUpdateChecks { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether descriptive text should be inserted into missing documentation headers.
        /// </summary>
        [JB::JetBrains.Util.XmlExternalizableAttribute(true)]
        public bool InsertTextIntoDocumentation { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether declaration comments should be multi line or single line.
        /// </summary>
        [JB::JetBrains.Util.XmlExternalizableAttribute(false)]
        public bool UseSingleLineDeclarationComments { get; set; }
        
        /// <summary>
        /// Gets or sets the last update check date.
        /// </summary>
        [JB::JetBrains.Util.XmlExternalizableAttribute("1900-01-01")]
        public string LastUpdateCheckDate { get; set; }

        /// <summary>
        /// Gets or sets the ParsingPerformance value.
        /// </summary>
        /// <value>
        /// The performance value.
        /// </value>
        [JB::JetBrains.Util.XmlExternalizableAttribute(9)]
        public int ParsingPerformance { get; set; }

        /// <summary>
        /// Gets the Scope that defines which store the data goes into.
        /// Must not be
        /// <c>0.</c>.
        /// </summary>
        /// <value>
        /// The Scope.
        /// </value>
        public XmlExternalizationScope Scope
        {
            get
            {
                return XmlExternalizationScope.UserSettings;
            }
        }

        /// <summary>
        /// Gets or sets the Specified Assembly Path.
        /// </summary>
        /// <value>
        /// The allow null attribute.
        /// </value>
        [JB::JetBrains.Util.XmlExternalizableAttribute("")]
        public string SpecifiedAssemblyPath { get; set; }

        /// <summary>
        /// Gets or sets the text for inserting suppressmessageattributes.
        /// </summary>
        [JB::JetBrains.Util.XmlExternalizableAttribute("Reviewed. Suppression is OK here.")]
        public string SuppressStyleCopAttributeJustificationText { get; set; }

        /// <summary>
        /// Gets the name of the tag.
        /// </summary>
        /// <value>
        /// The name of the tag.
        /// </value>
        public string TagName
        {
            get
            {
                return "StyleCopForReSharper";
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to use exclude from style cop setting.
        /// </summary>
        [JB::JetBrains.Util.XmlExternalizableAttribute(true)]
        public bool UseExcludeFromStyleCopSetting { get; set; }

        #endregion

        #region Public Methods

        /// <summary>
        /// Detects the style cop path.
        /// </summary>
        /// <returns>
        /// The path to the detected StyleCop assembly.
        /// </returns>
        public string DetectStyleCopPath()
        {
            var assemblyPath = GetStyleCopPath();
            return StyleCopReferenceHelper.LocationValid(assemblyPath) ? assemblyPath : null;
        }

        /// <summary>
        /// Gets the assembly location.
        /// </summary>
        /// <returns>
        /// The path to the StyleCop assembly.
        /// </returns>
        public string GetAssemblyPath()
        {
            if (!string.IsNullOrEmpty(this.SpecifiedAssemblyPath))
            {
                if (StyleCopReferenceHelper.LocationValid(this.SpecifiedAssemblyPath))
                {
                    return this.SpecifiedAssemblyPath;
                }

                // Location not valid. Blank it and automatically get location
                this.SpecifiedAssemblyPath = null;
            }

            var foundPath = this.DetectStyleCopPath();

            if (string.IsNullOrEmpty(foundPath))
            {
                MessageBox.Show(
                    string.Format("Failed to find the StyleCop {0} Assembly. Please check your StyleCop {0} installation.", Constants.SupportedStylecopVersion),
                    "Error Finding StyleCop Assembly.",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }

            return foundPath;
        }

        #endregion

        #region Implemented Interfaces

        #region IComponent

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        public void Init()
        {
        }

        #endregion

        #region IDisposable

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
        }

        #endregion

        #region IXmlExternalizable

        /// <summary>
        /// Reads the settings from the XML.
        /// </summary>
        /// <param name="element">
        /// The XmlElement to read from.
        /// </param>
        public void ReadFromXml(XmlElement element)
        {
            if (element == null)
            {
                return;
            }

            JB::JetBrains.Util.XmlExternalizationUtil.ReadFromXml(element, this);
        }

        /// <summary>
        /// Writes the settings to XML.
        /// </summary>
        /// <param name="element">
        /// The element.
        /// </param>
        public void WriteToXml(XmlElement element)
        {
            JB::JetBrains.Util.XmlExternalizationUtil.WriteToXml(element, this);
        }

        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// Gets the program files dir.
        /// </summary>
        /// <returns>
        /// The path to the Program Files directory.
        /// </returns>
        private static string GetProgramFilesDir()
        {
            return Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
        }

        /// <summary>
        /// Gets the StyleCop assembly path.
        /// </summary>
        /// <returns>
        /// The path to the StyleCop assembly.
        /// </returns>
        private static string GetStyleCopPath()
        {
            var programFilesDir = GetProgramFilesDir();
            var dirsNamedStyleCop = Directory.GetDirectories(programFilesDir, "*StyleCop "+ Constants.SupportedStylecopVersion + "*");

            var foundAssemblyPaths = GetStyleCopPaths(dirsNamedStyleCop);

            return foundAssemblyPaths.Count < 1 ? null : foundAssemblyPaths[0];
        }

        /// <summary>
        /// Searches the specified directories for the StyleCop assembly.
        /// </summary>
        /// <param name="possibleDirs">
        /// The possible dirs.
        /// </param>
        /// <returns>
        /// A list of the paths to the StyleCop assemblies found. 
        /// </returns>
        private static List<string> GetStyleCopPaths(ICollection<string> possibleDirs)
        {
            var styleCopPaths = new List<string>();

            if (possibleDirs.Count > 0)
            {
                foreach (var directory in possibleDirs)
                {
                    var styleCopAssemblyPath = Path.Combine(directory, StyleCopReferenceHelper.StyleCopAssemblyName);

                    if (File.Exists(styleCopAssemblyPath))
                    {
                        styleCopPaths.Add(styleCopAssemblyPath);
                    }
                }
            }

            return styleCopPaths;
        }

        #endregion
    }
}